/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.nodes; import java.util.ArrayList; import java.util.List; import java.util.Iterator; import java.util.HashMap; import java.util.HashSet; import java.util.Arrays; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import org.openide.util.Mutex; import org.openide.util.NotImplementedException; /** Index cookie providing operations useful for reordering * child nodes. {@link IndexedNode} is the common implementation. * * @author Jaroslav Tulach, Dafe Simonek */ public interface Index extends Node.Cookie { /** Get the number of nodes. * @return the count */ public int getNodesCount (); /** Get the child nodes. * @return array of nodes that can be sorted by this index */ public Node[] getNodes (); /** Get the index of a given node. * @param node node to find index of * @return index of the node, or <code>-1</code> if no such node was found */ public int indexOf (final Node node); /** Invoke a dialog for reordering the children. */ public void reorder (); /** Reorder all children with a given permutation. * @param perm permutation with the length of current nodes * @exception IllegalArgumentException if the permutation is not valid * @see Children#reorder */ public void reorder (int[] perm); /** Move the element at the <code>x</code>-th position to the <code>y</code>-th position. All * elements after the <code>y</code>-th position are moved down. * * @param x the position to remove the element from * @param y the position to insert the element to * @exception IndexOutOfBoundsException if an index is out of bounds */ public void move (int x, int y); /** Exchange two elements. * @param x position of the first element * @param y position of the second element * @exception IndexOutOfBoundsException if an index is out of bounds */ public void exchange (int x, int y); /** Move an element up. * @param x index of element to move up * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveUp (int x); /** Move an element down. * @param x index of element to move down * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveDown (int x); /** Add a new listener to the listener list. The listener will be notified of * any change in the order of the nodes. * * @param chl new listener */ public void addChangeListener (final ChangeListener chl); /** Remove a listener from the listener list. * * @param chl listener to remove */ public void removeChangeListener (final ChangeListener chl); /*********************** Inner classes ***********************/ /** A support class implementing some methods of the <code>Index</code> * cookie. */ public static abstract class Support implements Index { /** Registered listeners * @associates ChangeListener*/ private HashSet listeners; /** Default constructor. */ public Support () { } /* Moves element at x-th position to y-th position. All * elements after the y-th position are moved down. * * @param x the position to remove the element from * @param y the position to insert the element to * @exception IndexOutOfBoundsException if an index is out of bounds */ public void move (final int x, final int y) { int[] perm = new int[getNodesCount()]; for (int i = 0; i < perm.length; i++) { if (i >= x && i < y) { perm[i] = i - 1; } else { if (i >= y && i < x) { perm[i] = i + 1; } else { perm[i] = i; } } } perm[x] = y; perm[y] = x; reorder (perm); } /* Exchanges two elements. * @param x position of the first element * @param y position of the second element * @exception IndexOutOfBoundsException if an index is out of bounds */ public void exchange (final int x, final int y) { int[] perm = new int[getNodesCount ()]; for (int i = 0; i < perm.length; i++) { perm[i] = i; } perm[x] = y; perm[y] = x; reorder (perm); } /* Moves element up. * @param x index of element to move up * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveUp (final int x) { exchange (x, x - 1); } /* Moves element down. * @param x index of element to move down * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveDown (final int x) { exchange (x, x + 1); } /* Adds new listener to the listener list. Listener is notified of * any change in ordering of nodes. * * @param chl new listener */ public void addChangeListener (final ChangeListener chl) { if (listeners == null) listeners = new HashSet(); listeners.add(chl); } /* Removes listener from the listener list. * Removed listener isn't notified no more. * * @param chl listener to remove */ public void removeChangeListener (final ChangeListener chl) { if (listeners == null) return; listeners.remove(chl); } /** Fires notification about reordering to all * registered listeners. * * @param che change event to fire off */ protected void fireChangeEvent (ChangeEvent che) { if (listeners == null) return; HashSet cloned; // clone listener list synchronized (this) { cloned = (HashSet)listeners.clone(); } // fire on cloned list to prevent from modifications when firing for (Iterator iter = cloned.iterator(); iter.hasNext(); ) { ((ChangeListener)iter.next()).stateChanged(che); } } /** Get the nodes; should be overridden if needed. * @return the nodes */ public abstract Node[] getNodes (); /** Get the index of a node. Simply scans through the array returned by {@link #getNodes}. * @param node the node * @return the index, or <code>-1</code> if the node was not found */ public int indexOf (final Node node) { Node[] arr = getNodes (); for (int i = 0; i < arr.length; i++) { if (node.equals (arr[i])) { return i; } } return -1; } /** Reorder the nodes with dialog; should be overridden if needed. */ public void reorder () { IndexedCustomizer ic = new IndexedCustomizer (); ic.setObject (this); ic.show (); } /** Get the node count. Subclasses must provide this. * @return the count */ public abstract int getNodesCount (); /** Reorder by permutation. Subclasses must provide this. * @param perm the permutation */ public abstract void reorder (int[] perm); } // end of Support inner class /** Reorderable children list stored in an array. */ public static class ArrayChildren extends Children.Array implements Index { /** Support instance for delegation of some <code>Index</code> methods. */ protected Index support; /** Constructor for the support. */ public ArrayChildren () { this (null); } /** Constructor. * @param ar the array */ private ArrayChildren (List ar) { super (ar); // create support instance for delegation of common tasks support = new Support() { public Node[] getNodes () { return ArrayChildren.this.getNodes (); } public int getNodesCount () { return ArrayChildren.this.getNodesCount(); } public void reorder (int[] perm) { ArrayChildren.this.reorder(perm); fireChangeEvent(new ChangeEvent(ArrayChildren.this)); } }; } /** If default constructor is used, then this method is called to lazily create * the collection. Even it claims that it returns Collection only subclasses * of List are valid values. * <P> * This implementation returns ArrayList. * * @return any List collection. */ protected java.util.Collection initCollection () { return new ArrayList (); } /* Reorders all children with given permutation. * @param perm permutation with the length of current nodes * @exception IllegalArgumentException if the perm is not valid permutation */ public void reorder (final int[] perm) { MUTEX.writeAccess (new Runnable () { public void run () { Object[] n = nodes.toArray (); List l = (List)nodes; for (int i = 0; i < n.length; i++) { l.set (perm[i], n[i]); } refresh (); } }); } /** Invokes a dialog for reordering children using {@link IndexedCustomizer}. */ public void reorder () { IndexedCustomizer ic = new IndexedCustomizer(); ic.setObject(this); ic.show(); } /* Moves element at x-th position to y-th position. All * elements after the y-th position are moved down. * Delegates functionality to Index.Support. * * @param x the position to remove the element from * @param y the position to insert the element to * @exception IndexOutOfBoundsException if an index is out of bounds */ public void move (final int x, final int y) { support.move(x, y); } /* Exchanges two elements. * Delegates functionality to Index.Support. * @param x position of the first element * @param y position of the second element * @exception IndexOutOfBoundsException if an index is out of bounds */ public void exchange (final int x, final int y) { support.exchange(x, y); } /* Moves element up. * Delegates functionality to Index.Support. * @param x index of element to move up * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveUp (final int x) { support.exchange(x, x - 1); } /* Moves element down. * Delegates functionality to Index.Support. * @param x index of element to move down * @exception IndexOutOfBoundsException if an index is out of bounds */ public void moveDown (final int x) { support.exchange(x, x + 1); } /* Returns the index of given node. * @param node Node to find index of. * @return Index of the node, -1 if no such node was found. */ public int indexOf (final Node node) { Integer result = (Integer)MUTEX.writeAccess (new Mutex.Action () { public Object run () { return new Integer(((List)nodes).indexOf(node)); } }); return result.intValue(); } /* Adds new listener to the listener list. Listener is notified of * any change in ordering of nodes. * * @param chl new listener */ public void addChangeListener (final ChangeListener chl) { support.addChangeListener(chl); } /* Removes listener from the listener list. * Removed listener isn't notified no more. * * @param chl listener to remove */ public void removeChangeListener (final ChangeListener chl) { support.removeChangeListener(chl); } } // End of ArrayChildren inner class } /* * Log * 11 Gandalf 1.10 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 10 Gandalf 1.9 9/17/99 Jaroslav Tulach Reorder of nodes works. * 9 Gandalf 1.8 8/27/99 Jaroslav Tulach * 8 Gandalf 1.7 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 7 Gandalf 1.6 4/16/99 Jaroslav Tulach Changes in children. * 6 Gandalf 1.5 3/22/99 Jesse Glick [JavaDoc] * 5 Gandalf 1.4 3/21/99 Jaroslav Tulach Repository displayed ok. * 4 Gandalf 1.3 3/17/99 Jesse Glick [JavaDoc] * 3 Gandalf 1.2 3/17/99 Jesse Glick [JavaDoc] * 2 Gandalf 1.1 2/16/99 David Simonek * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ */